﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JOthello.Forms;
using Common.Model;
using System.Windows.Forms;
using System.Drawing;
using Common.Extensions;
using System.Diagnostics;
using System.Threading;

namespace JOthello.Controls
{
    public class SpielControl
    {
        private Button[,] fieldBtns;
        private WaitForm waitForm = new WaitForm("I\'m thinking");
        private List<CSpieler> sps;
        private CSpielfeld.GameMode mode;
        private int size;
        private Action<CPoint> turned;
        private CSpieler networkPlayer;
        private bool showPossible;

        private Bitmap BackgroundBlack;
        private Bitmap BackgroundWhite;
        private Bitmap Background;
        private Bitmap BackgroundPossible;

        #region getter / setter
        private SpielForm Spielform { get; set; }
        #endregion

        #region konstruktor
        public SpielControl(SpielForm form, List<CSpieler> sps, CSpielfeld.GameMode mode, int size, bool showPossible, Action<CPoint> turned = null, CSpieler networkPlayer = null, CSpieler current = null)
        {
            this.Spielform = form;
            this.sps = sps;
            this.mode = mode;
            this.size = size;
            this.fieldBtns = new Button[size, size];
            this.turned = turned;
            this.networkPlayer = networkPlayer;
            this.showPossible = showPossible;

            CSpielmanager.Instance.startGame(sps, spielFertig, showPossible, mode, size, current);

            commonConstruct();
        }
        public SpielControl(SpielForm form, string path)
        {
            this.Spielform = form;

            try
            {
                CSpielmanager.Instance.import(spielFertig, path);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                this.Spielform.Close();
                return;
            }

            this.sps = CSpielmanager.Instance.Spieler;
            this.mode = CSpielfeld.Instance.Mode;
            this.size = (int)Math.Sqrt(CSpielfeld.Instance.Feld.Length);
            this.fieldBtns = new Button[size, size];
            this.showPossible = CSpielmanager.Instance.ShowPossibleMoves;

            commonConstruct();
        }
        private void commonConstruct()
        {
            generateBackgrounds();
            generateSpielfeld();
            feldResized();
            reMakeSpielfeld();

            Spielform.Resize += (s, e) => feldResized();

            Spielform.BtnExport.Click += new EventHandler(BtnExport_Click);
            Spielform.BtnUndo.Click += (s, e) => { CSpielmanager.Instance.undo(); reMakeSpielfeld(); };
            Spielform.BtnRedo.Click += (s, e) => { CSpielmanager.Instance.redo(); reMakeSpielfeld(); };

            Spielform.MinimumSize = new Size(Spielform.PnlSpieler0.Width + Spielform.PnlSpieler1.Width + 200, Spielform.BtnSpieler0Next.Location.Y + Spielform.BtnExport.Height + 80);
        }

        #endregion

        /// <summary>
        /// zeichnet die hintergründe
        /// </summary>
        private void generateBackgrounds()
        {
            int padding = 20;
            Graphics g;

            Brush baseBrush = new SolidBrush(Properties.Settings.Default.BackgroundColor);

            Background = new Bitmap(200, 200);
            BackgroundWhite = new Bitmap(200, 200);
            BackgroundBlack = new Bitmap(200, 200);

            Rectangle baseRect = new Rectangle(0, 0, Background.Width, Background.Height);

            #region normal background
            g = Graphics.FromImage(Background);
            g.FillRectangle(baseBrush, baseRect);
            #endregion

            #region possible background
            if (this.showPossible)
            {
                BackgroundPossible = new Bitmap(200, 200);
                g = Graphics.FromImage(BackgroundPossible);
                g.FillRectangle(baseBrush, baseRect);
                g.FillEllipse(new SolidBrush(Properties.Settings.Default.PossibleColor), new Rectangle(padding / 2, padding / 2, Background.Width - padding, Background.Height - padding));
            }
            else
            {
                BackgroundPossible = Background;
            }
            #endregion

            #region black background
            g = Graphics.FromImage(BackgroundBlack);
            g.FillRectangle(baseBrush, baseRect);
            g.FillEllipse(Brushes.Black, new Rectangle(padding / 2, padding / 2, Background.Width - padding, Background.Height - padding));
            #endregion

            #region white background
            g = Graphics.FromImage(BackgroundWhite);
            g.FillRectangle(baseBrush, baseRect);
            g.FillEllipse(Brushes.White, new Rectangle(padding / 2, padding / 2, Background.Width - padding, Background.Height - padding));
            #endregion
        }

        /// <summary>
        /// generiert ein erstes spielfeld
        /// </summary>
        private void generateSpielfeld()
        {
            #region spielfeld btns
            CSpielfeld.Instance.AllPoints.ForEach((currentPoint) =>
            {
                Button btn = new Button();
                /* size und locatin nur dummy, wird dannach in resize gesetzt. */
                btn.Size = new Size(10, 10);
                btn.Location = new Point(10, 10);
                btn.Name = currentPoint.X.ToString() + "##" + currentPoint.Y.ToString();
                btn.Enabled = false;
                btn.Margin = new Padding(0);
                btn.FlatStyle = FlatStyle.Flat;
                btn.FlatAppearance.BorderSize = 1;
                btn.FlatAppearance.BorderColor = Properties.Settings.Default.SeperatorColor;
                btn.BackColor = Color.Transparent;
                btn.BackgroundImageLayout = ImageLayout.Stretch;
                btn.Tag = CSpielfeld.FieldColor.NotSetNotPossible;
                btn.Click += new EventHandler(btnSpielfeld_Click);
                Spielform.PnlFeld.Controls.Add(btn);
                fieldBtns[currentPoint.Y, currentPoint.X] = btn;
            });
            #endregion

            Spielform.PbxSpieler0.SizeMode = Spielform.PbxSpieler1.SizeMode = PictureBoxSizeMode.Zoom;
            Spielform.PbxSpieler0.Image = sps[0].Bild;
            Spielform.PbxSpieler1.Image = sps[1].Bild;

            Spielform.PnlSpieler0.BackColor = (sps[0].Farbe == CSpielfeld.FieldColor.Black) ? Color.Black : Color.White;
            Spielform.PnlSpieler1.BackColor = (sps[1].Farbe == CSpielfeld.FieldColor.Black) ? Color.Black : Color.White;

            Spielform.LblSpieler0Name.Text = sps[0].Name;
            Spielform.LblSpieler1Name.Text = sps[1].Name;

            Spielform.BtnSpieler0Next.Visible = Spielform.BtnSpieler1Next.Visible = false;

            Spielform.BtnSpieler0Next.Click += (s, e) => getNextTurnFromKI();
            Spielform.BtnSpieler1Next.Click += (s, e) => getNextTurnFromKI();

            Spielform.BtnExport.Visible = Spielform.BtnExport.Enabled = networkPlayer == null;
        }

        /// <summary>
        /// en- / disablet die buttons und setzt entsprechende hintergrundbilder, setzt zur zeit am zug, aktualisiert punkte etc
        /// </summary>
        private void reMakeSpielfeld()
        {
            bool currentIsNotMe = false, currentIsNetworkPlayer = false;
            if (CSpielmanager.Instance.CurrentSpieler != null)
            {
                currentIsNotMe = CSpielmanager.Instance.CurrentSpieler.GetType() == typeof(CKISpieler) || (currentIsNetworkPlayer = CSpielmanager.Instance.CurrentSpieler.Equals(networkPlayer));

                Spielform.BtnSpieler0Next.Visible = Spielform.BtnSpieler0Next.Enabled = sps[0].Equals(CSpielmanager.Instance.CurrentSpieler) && currentIsNotMe && !currentIsNetworkPlayer;
                Spielform.BtnSpieler1Next.Visible = Spielform.BtnSpieler1Next.Enabled = sps[1].Equals(CSpielmanager.Instance.CurrentSpieler) && currentIsNotMe && !currentIsNetworkPlayer;
                Spielform.LblSpieler1AmZug.Visible = !(Spielform.LblSpieler0AmZug.Visible = sps[0].Equals(CSpielmanager.Instance.CurrentSpieler));
            }

            #region re/enable button
            CSpielfeld.Instance.AllPoints.ForEach((currentPoint) =>
            {
                Button currentBtn = fieldBtns[currentPoint.Y, currentPoint.X];

                CSpielfeld.FieldColor col;

                currentBtn.Tag = col = CSpielfeld.Instance[currentPoint];

                currentBtn.Enabled = !currentIsNotMe && (col == CSpielfeld.FieldColor.Possible || !this.showPossible);

                setBtnCol(currentBtn);
            });
            #endregion
            
            Spielform.LblSpieler0Punkte.Text = sps[0].Punkte.ToString() + " Punkte";
            Spielform.LblSpieler1Punkte.Text = sps[1].Punkte.ToString() + " Punkte";

            #region fokus setzen

            if (Spielform.BtnSpieler0Next.Enabled) Spielform.BtnSpieler0Next.Focus();
            else if (Spielform.BtnSpieler1Next.Enabled) Spielform.BtnSpieler1Next.Focus();
            else Spielform.PnlSpieler0.Focus();
            #endregion

            Spielform.BtnRedo.Visible = Spielform.BtnRedo.Enabled = CSpielmanager.Instance.Protokoll.hasNext && networkPlayer == null;
            Spielform.BtnUndo.Visible = Spielform.BtnUndo.Enabled = CSpielmanager.Instance.Protokoll.hasPrev && networkPlayer == null;
        }

        /// <summary>
        /// setzt das hintergrundbild eines buttons entsprechend seines tags
        /// </summary>
        /// <param name="btn"></param>
        private void setBtnCol(Button btn)
        {
            switch ((CSpielfeld.FieldColor)btn.Tag)
            {
                case CSpielfeld.FieldColor.NotSetNotPossible:
                    btn.BackgroundImage = Background;
                    break;
                case CSpielfeld.FieldColor.Possible:
                    btn.BackgroundImage = BackgroundPossible;
                    break;
                case CSpielfeld.FieldColor.White:
                    btn.BackgroundImage = BackgroundWhite;
                    break;
                case CSpielfeld.FieldColor.Black:
                    btn.BackgroundImage = BackgroundBlack;
                    break;
            }
        }

        #region events

        private void BtnExport_Click(object sender, EventArgs e)
        {
            if (CSpielmanager.Instance.Protokoll.Pfad == null)
            {
                SaveFileDialog sfd = new SaveFileDialog()
                {
                    AddExtension = true,
                    CheckFileExists = false,
                    CheckPathExists = false,
                    FileName = "JOthello Spiel",
                    Filter = "JOthello Spiel|*" + CProtokoll.suffix,
                    Title = "JOthello Spiel speichern"
                };

                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    CSpielmanager.Instance.Protokoll.Pfad = sfd.FileName;
                }
            }
            if (CSpielmanager.Instance.Protokoll.Pfad != null)
            {
                CSpielmanager.Instance.SaveProtokollAsync(CSpielmanager.Instance.Protokoll.Pfad);
            }
            
        }

        private void btnSpielfeld_Click(object sender, EventArgs e)
        {
            Button s = sender as Button;
            if (s != null)
            {
                string[] sNames = s.Name.Split(new string[] { "##" }, StringSplitOptions.RemoveEmptyEntries);
                gotNextTurn(new CPoint(int.Parse(sNames[0]), int.Parse(sNames[1])));
            }
        }
        #endregion

        /// <summary>
        /// passt die feldgrösse an
        /// </summary>
        private void feldResized()
        {
            int size = (Spielform.PnlFeld.Width > Spielform.PnlFeld.Height ? Spielform.PnlFeld.Height : Spielform.PnlFeld.Width) / CSpielfeld.Instance.Size;
            int marginLeft = (Spielform.PnlFeld.Width - (CSpielfeld.Instance.Size * size)) / 2;
            int marginTop = 0;
            CSpielfeld.Instance.AllPoints.ForEach((currentPoint) =>
            {
                Button btn = fieldBtns[currentPoint.Y, currentPoint.X];
                if (btn != null)
                {
                    btn.Size = new Size(size, size);
                    btn.Location = new Point(currentPoint.X * size + marginLeft, currentPoint.Y * size + marginTop);
                    setBtnCol(btn);
                }
            });
        }

        /// <summary>
        /// fordert den nächsten zug des ki's an
        /// </summary>
        private void getNextTurnFromKI()
        {
            if (CSpielmanager.Instance.SpielFertig)
                return;

            CKISpieler s = CSpielmanager.Instance.CurrentSpieler as CKISpieler;
            if (s != null)
            {
                s.getNextTurnAsync(gotNextTurn);
                if (waitForm == null || waitForm.IsDisposed) waitForm = new WaitForm("I\'m thinking");

                if (!waitForm.Visible) waitForm.ShowDialog();
            }

        }

        /// <summary>
        /// vollzieht den nächsten zug
        /// </summary>
        /// <param name="p"></param>
        public void gotNextTurn(CPoint p)
        {
            if (waitForm != null && !waitForm.IsDisposed)
            {
                waitForm.Visible = false;
                waitForm.Close();
            }
            if (turned != null && !CSpielmanager.Instance.CurrentSpieler.Equals(networkPlayer)) turned(p);
            if (!CSpielmanager.Instance.turn(p))
            {
                MessageBox.Show("Der Zug " + p.ToString(true, true) + " scheint ungültig zu sein." + Environment.NewLine + "Legen Sie ihren Stein an einem anderen, möglichen Punkt.", "Ungültiger Zug", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            reMakeSpielfeld();
        }

        /// <summary>
        /// startet das spiel erneut
        /// </summary>
        private void replay()
        {
            CSpielmanager.Instance.startGame(sps, spielFertig, showPossible, mode, size);

            reMakeSpielfeld();
        }

        /// <summary>
        /// tritt ein, wenn das spiel beendet ist
        /// </summary>
        /// <param name="sps"></param>
        /// <param name="unentschieden"></param>
        private void spielFertig(List<CSpieler> sps, bool unentschieden)
        {
            if (waitForm != null && !waitForm.IsDisposed)
            {
                waitForm.Visible = false;
                waitForm.Close();
            }

            reMakeSpielfeld();

            DialogResult result = (new SpielFertigForm(sps, unentschieden, networkPlayer == null)).ShowDialog();
            if (result == DialogResult.Retry)
            {
                CSpielmanager.Instance.removeAllSpieler();
                replay();
            }
        }
    }
}
